package utils
{
	import flash.display3D.Context3D;
	import flash.display3D.Program3D;
	import flash.display3D.textures.Texture;
	
	public class Game_actor extends Molehill_entity
	{
		// game-related stats
		public var name:String = ''; // unique
		public var classname:String = ''; // not unique
		public var owner:Game_actor; // so you can't shoot yourself
		public var touching:Game_actor; // last collision detected
		public var active:Boolean = true; // animate?
		public var visible:Boolean = true; // render?
		public var health:Number = 1; // when zero, die
		public var damage:Number = 250; // inflicted on others
		public var points:Number = 25; // score earned if destroyed
		public var collides:Boolean = false; // check hits?
		public var collidemode:uint = 0; // 0=sphere, 1=aabb
		public var radius:Number = 1; // used for sphere collision
		public var aabb_min:Vector3D = new Vector3D(1, 1, 1, 1);
		public var aabb_max:Vector3D = new Vector3D(1, 1, 1, 1);
		
		// callback functions
		public var run_constantly:Function;
		public var run_constantly_delay:uint = 1000;
		public var run_when_no_health:Function;
		public var run_when_max_age:Function;
		public var run_when_created:Function;
		// time-related vars
		public var age:uint = 0;
		public var age_max:uint = 0;
		public var step_counter:uint = 0;
		// animation vars in units per SECOND (1000ms)
		public var pos_velocity:Vector3D;
		public var rot_velocity:Vector3D;
		public var scale_velocity:Vector3D;
		public var tint_velocity:Vector3D;
		// automatically shoot bullets when 'near' an enemy
		public var bullets:Game_actorpool;
		public var shoot_name:String = '';
		public var shoot_delay:uint = 4000;
		public var shoot_next:uint = 0;
		public var shoot_random_delay:Number = 2000;
		public var shoot_dist:Number = 100;
		public var shoot_at:Game_actor = null;
		public var shoot_velocity:Number = 50;
		public var shoot_sound:Sound;
		// spawnable particles
		public var particles:Game_particlesystem;
		public var spawn_constantly:String = '';
		public var spawn_constantly_delay:uint = 0;
		public var spawn_constantly_next:uint = 0;
		public var spawn_when_no_health:String = '';
		public var spawn_when_max_age:String = '';
		public var spawn_when_created:String = '';
		// sound effects
		public var sound_constantly_delay:uint = 1000;
		public var sound_constantly_next:uint = 0;
		public var sound_constantly:Sound;
		public var sound_when_no_health:Sound;
		public var sound_when_max_age:Sound;
		public var sound_when_created:Sound;
		
		public function Game_actor(mydata:Class=null, mycontext:Context3D=null, myshader:Program3D=null, mytexture:Texture=null, data_is_zxy:Boolean=true, texture_flip:Boolean=true)
		{
			super(mydata, mycontext, myshader, mytexture, data_is_zxy, texture_flip);
		}
		public function step(ms:uint):void
		{
			if (!active) return;
			age += ms;
			step_counter++;
			if (health <= 0)
			{
				//trace(name + " out of health.");
				if (particles && spawn_when_no_health)
					particles.spawn(spawn_when_no_health, transform);
				if (sound_when_no_health)
					sound_when_no_health.play();
				if (run_when_no_health != null)
					run_when_no_health();
				die();
				return;
			}
			if ((age_max != 0) && (age >= age_max))
			{
				//trace(name + " old age.");
				if (particles && spawn_when_max_age)
					particles.spawn(spawn_when_max_age, transform);
				if (sound_when_max_age)
					sound_when_max_age.play();
				if (run_when_max_age != null)
					run_when_max_age();
				die();
				return;
			}
			if (pos_velocity)
			{
				x += pos_velocity.x * (ms / 1000);
				y += pos_velocity.y * (ms / 1000);
				z += pos_velocity.z * (ms / 1000);
			}
			if (rot_velocity)
			{
				rotationDegreesX += rot_velocity.x * (ms / 1000);
				rotationDegreesY += rot_velocity.y * (ms / 1000);
				rotationDegreesZ += rot_velocity.z * (ms / 1000);
			}
			if (scale_velocity)
			{
				scaleX += scale_velocity.x * (ms / 1000);
				scaleY += scale_velocity.y * (ms / 1000);
				scaleZ += scale_velocity.z * (ms / 1000);
			}
			
			
			// maybe spawn a new particle
			if (visible && particles && spawn_constantly_delay > 0)
			{
				if (spawn_constantly != '')
				{
					if (age >= spawn_constantly_next)
					{
						//trace("actor spawn " + spawn_constantly);
						spawn_constantly_next = age + spawn_constantly_delay;
						particles.spawn(spawn_constantly, transform);
					}
				}
			}
			// maybe trigger a sound
			if (visible && sound_constantly_delay > 0)
			{
				if (sound_constantly)
				{
					if (age >= sound_constantly_next)
					{
						sound_constantly_next = age + sound_constantly_delay;
						sound_constantly.play();
					}
				}
			}
			
			// maybe "shoot" (spawn an actor)
			if (visible && bullets && (shoot_name != ''))
			{
				var should_shoot:Boolean = false;
				if (age >= shoot_next)
				{
					shoot_next = age + shoot_delay + (Math.random() * shoot_random_delay);
					if (shoot_dist < 0)
						should_shoot = true;
					else if (shoot_at &&(shoot_dist > 0) &&(Vector3D.distance(position, shoot_at.position) <= shoot_dist))
					{
						should_shoot = true;
					}
					if (should_shoot)
					{
						var b:Game_actor = bullets.spawn(shoot_name, transform);
						// remember who this bullet belongs to
						b.owner = this;
						// aim towards an enemy?
						if (shoot_at)
						{
							b.transform.pointAt(shoot_at.transform.position);
							b.rotationDegreesY -= 90;
							b.pos_velocity = b.transform.position.subtract(shoot_at.transform.position);
							b.pos_velocity.normalize();
							b.pos_velocity.negate();
							b.pos_velocity.scaleBy(shoot_velocity);
						}
						// otherwise we simply fire in whatever
						// direction was given during spawn
						// when the game set pos_velocity
						if (shoot_sound)
							shoot_sound.play();
					}
				}
			}
		}
		
		public function cloneactor():Game_actor
		{
			var myclone:Game_actor = new Game_actor();
			updateTransformFromValues();
			myclone.transform = this.transform.clone();
			myclone.updateValuesFromTransform();
			myclone.mesh = this.mesh;
			myclone.texture = this.texture;
			myclone.shader = this.shader;
			myclone.vertexBuffer = this.vertexBuffer;
			myclone.indexBuffer = this.indexBuffer;
			myclone.context = this.context;
			myclone.polycount = this.polycount;
			myclone.blend_src = this.blend_src;
			myclone.blend_dst = this.blend_dst;
			myclone.culling_mode = this.culling_mode;
			myclone.depth_test_mode = this.depth_test_mode;
			myclone.depth_test = this.depth_test;
			myclone.depth_draw = this.depth_draw;
			// game-related stats
			myclone.name = this.name;
			myclone.classname = this.classname;
			myclone.owner = this.owner;
			myclone.active = this.active;
			myclone.visible = this.visible;
			myclone.health = this.health;
			myclone.damage = this.damage;
			myclone.points = this.points;
			myclone.collides = this.collides;
			myclone.radius = this.radius;
			myclone.aabb_min = this.aabb_min.clone();
			myclone.aabb_max = this.aabb_max.clone();
			// callback functions
			myclone.run_constantly = this.run_constantly;
			myclone.run_constantly_delay = this.run_constantly_delay;
			myclone.run_when_no_health = this.run_when_no_health;
			myclone.run_when_max_age = this.run_when_max_age;
			myclone.run_when_created = this.run_when_created;
			// time-related vars
			myclone.age = this.age;
			myclone.age_max = this.age_max;
			myclone.step_counter = this.step_counter;
			// animation-related vars - per ms
			myclone.pos_velocity = this.pos_velocity;
			myclone.rot_velocity = this.rot_velocity;
			myclone.scale_velocity = this.scale_velocity;
			myclone.tint_velocity = this.tint_velocity;
			// bullets
			myclone.bullets = this.bullets;
			myclone.shoot_name = this.shoot_name;
			myclone.shoot_delay = this.shoot_delay;
			myclone.shoot_next = this.shoot_next;
			myclone.shoot_random_delay = this.shoot_random_delay;
			myclone.shoot_dist = this.shoot_dist;
			myclone.shoot_at = this.shoot_at;
			myclone.shoot_velocity = this.shoot_velocity;
			myclone.shoot_sound = this.shoot_sound;
			// spawnable particles
			myclone.particles = this.particles;
			myclone.spawn_constantly = this.spawn_constantly;
			myclone.spawn_constantly_delay = this.spawn_constantly_delay;
			myclone.spawn_constantly_next = this.spawn_constantly_next;
			myclone.spawn_when_no_health = this.spawn_when_no_health;
			myclone.spawn_when_max_age = this.spawn_when_max_age;
			myclone.spawn_when_created = this.spawn_when_created;
			// sound effects
			myclone.sound_constantly_delay = this.sound_constantly_delay;
			myclone.sound_constantly_next = this.sound_constantly_next;
			myclone.sound_constantly = this.sound_constantly;
			myclone.sound_when_no_health = this.sound_when_no_health;
			myclone.sound_when_max_age = this.sound_when_max_age;
			myclone.sound_when_created = this.sound_when_created;
			myclone.active = true;
			myclone.visible = true;
			return myclone;
		}
		
		public function die():void
		{
			//trace(name + " dies!");
			active = false;
			visible = false;
		}
		
		public function respawn(pos:Matrix3D = null):void
		{
			age = 0;
			step_counter = 0;
			active = true;
			visible = true;
			// don't shoot immediately
			shoot_next = Math.random() * shoot_random_delay;
			if (pos)
			{
				transform = pos.clone();
			}
			if (sound_when_created)
				sound_when_created.play();
			if (run_when_created != null)
				run_when_created();
			if (particles && spawn_when_created)
				particles.spawn(spawn_when_created, transform);
			//trace("Respawned " + name + " at " + pos_string());
		}
		
		// used for collision callback performed in Game_actorpool
		public function colliding(checkme:Game_actor):Game_actor
		{
			if (is_colliding_sphere(checkme))
				return checkme;
			else
				return null;
		}
		
		// simple sphere to sphere collision
		public function is_colliding_sphere(checkme:Game_actor):Boolean
		{
			// never collide with yourself
			if (this == checkme) return false;
			// only check if these shapes are collidable
			if (!collides || !checkme.collides) return false;
			// don't check your own bullets
			if (checkme.owner == this) return false;
			// don't check if no radius
			if (radius == 0 || checkme.radius == 0) return false;
			var dist:Number = Vector3D.distance(position, checkme.position);
			if (dist <= (radius+checkme.radius))
			{
				// trace("Collision detected at distance="+dist);
				touching = checkme; // remember who hit us
				return true;
			}
			// default: too far away
			// trace("No collision. Dist = "+dist);
			return false;
		}
		
		// axis-aligned bounding box collision detection
		// not used in the example game but here for convenience
		private function aabb_collision(min1:Vector3D, max1:Vector3D,min2:Vector3D, max2:Vector3D ):Boolean
		{
			if ( min1.x > max2.x ||
				min1.y > max2.y ||
				min1.z > max2.z ||
				max1.x < min2.x ||
				max1.y < min2.y ||
				max1.z < min2.z )
			{
				return false;
			}
			return true;
		}
		
		public function is_colliding_aabb(checkme:Game_actor):Boolean
		{
			// never collide with yourself
			if (this == checkme) return false;
			// only check if these shapes are collidable
			if (!collides || !checkme.collides) return false;
			// don't check your own bullets
			if (checkme.owner == this) return false;
			// don't check if no aabb data
			if (aabb_min == null ||
				aabb_max == null ||
				checkme.aabb_min == null ||
				checkme.aabb_max == null)
				return false;
			if (aabb_collision(
				position + aabb_min,
				position + aabb_max,
				checkme.position + checkme.aabb_min,
				checkme.position + checkme.aabb_max))
			{
				touching = checkme; // remember who hit us
				return true;
			}
			// trace("No collision.");
			return false;
		}
		
		/**end class*/
	}
}